﻿using System;
using System.Collections.Generic;
using UnityEditor;
using Object = UnityEngine.Object;

namespace HK.Editor.GUIEx.Property
{
    /// <summary>
    /// 序列化数据排序类
    /// </summary>
    public static class PropertyArraySort
    {
			/// <summary>
			/// 排序比较器
			/// </summary>
			/// <param name="iP1">Property 1</param>
			/// <param name="iP2">Property 2</param>
			private delegate int SortComparer(SerializedProperty iP1, SerializedProperty iP2);
			
			/// <summary>
			/// 排序 - 冒泡算法
			/// <para>* 按指定Property名的Element排序</para>
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iDescending">倒序标志位</param>
			/// <param name="iPropertyName">排序用的Property名</param>
			public static void SortOnProperty(SerializedProperty iList, int iLength, bool iDescending, string iPropertyName) 
			{

				// 冒泡排序
				BubbleSort(iList, iLength, 
					(iP1, iP2) => 
				{

					var a = iP1.FindPropertyRelative(iPropertyName);
					var b = iP2.FindPropertyRelative(iPropertyName);
					if (null != a && null != b && a.propertyType == b.propertyType) 
					{
						var comparison = Compare(a, b, a.propertyType);
						return iDescending ? -comparison : comparison;
					}

					return 0;
				});
			}
			
			/// <summary>
			/// 排序 - 冒泡算法
			/// <para>* 按指定Property名的Element排序</para>
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iDescending">倒序标志位</param>
			/// <param name="iPropertyName">排序用的Property名</param>
			public static void SortOnProperty(List<SerializedProperty> iList, int iLength, bool iDescending, string iPropertyName) 
			{

				// 冒泡排序
				BubbleSort(iList, iLength, 
					(iP1, iP2) => 
					{

						var a = iP1.FindPropertyRelative(iPropertyName);
						var b = iP2.FindPropertyRelative(iPropertyName);
						if (null != a && null != b && a.propertyType == b.propertyType) 
						{
							var comparison = Compare(a, b, a.propertyType);
							return iDescending ? -comparison : comparison;
						}

						return 0;
					});
			}

			/// <summary>
			/// 排序 - 冒泡算法
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iDescending">倒序标志位</param>
			/// <param name="iType">类型</param>
			public static void SortOnType(SerializedProperty iList, int iLength, bool iDescending, SerializedPropertyType iType) 
			{

				BubbleSort(iList, iLength, (iP1, iP2) => {

					var comparision = Compare(iP1, iP2, iType);
					return iDescending ? -comparision : comparision;
				});
			}
			
			/// <summary>
			/// 排序 - 冒泡算法
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iDescending">倒序标志位</param>
			/// <param name="iType">类型</param>
			public static void SortOnType(List<SerializedProperty> iList, int iLength, bool iDescending, SerializedPropertyType iType) 
			{

				BubbleSort(iList, iLength, (iP1, iP2) => {

					var comparision = Compare(iP1, iP2, iType);
					return iDescending ? -comparision : comparision;
				});
			}
			
			/// <summary>
			/// 冒泡排序
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iComparer">比较器</param>
			private static void BubbleSort(SerializedProperty iList, int iLength, SortComparer iComparer) 
			{

				for (var i = 0; i < iLength; ++i) 
				{
					var pI = iList.GetArrayElementAtIndex(i);
					for (var j = i + 1; j < iLength; ++j) 
					{
						var pJ = iList.GetArrayElementAtIndex(j);
						if (iComparer(pI, pJ) > 0) 
						{
							// 升序 : 移动 J -> I
							iList.MoveArrayElement(j, i);
						}
					}
				}
			}
			
			/// <summary>
			/// 冒泡排序
			/// </summary>
			/// <param name="iList">排序列表</param>
			/// <param name="iLength">长度</param>
			/// <param name="iComparer">比较器</param>
			private static void BubbleSort(List<SerializedProperty> iList, int iLength, SortComparer iComparer) 
			{
				for (var i = 0; i < iLength; ++i) 
				{
					var pI = iList[i];
					for (var j = i + 1; j < iLength; ++j) 
					{
						var pJ = iList[j];
						// 升序 : 移动 J -> I
						if (iComparer(pI, pJ) > 0)
						{
							(iList[i], iList[j]) = (iList[j], iList[i]);
						}
					}
				}
			}

			/// <summary>
			/// 比较函数
			/// </summary>
			/// <param name="iP1">Property P1</param>
			/// <param name="iP2">Property P2</param>
			/// <param name="iType">类型</param>
			/// <returns>-1:P1大于P2; 0:P1等于P2; 1:P1小于P2;</returns>
			private static int Compare(SerializedProperty iP1, SerializedProperty iP2, SerializedPropertyType iType) 
			{

				if (null == iP1 || null == iP2) return 0;
				switch (iType) 
				{
					case SerializedPropertyType.Boolean:
						return iP1.boolValue.CompareTo(iP2.boolValue);
					case SerializedPropertyType.Character:
					case SerializedPropertyType.Enum:
					case SerializedPropertyType.Integer:
					case SerializedPropertyType.LayerMask:
						return iP1.longValue.CompareTo(iP2.longValue);
					case SerializedPropertyType.Color:
						return iP1.colorValue.grayscale.CompareTo(iP2.colorValue.grayscale);
					
					// 场景中另一个对象的引用
					case SerializedPropertyType.ExposedReference:
						return CompareObjects(iP1.exposedReferenceValue, iP2.exposedReferenceValue);
					case SerializedPropertyType.Float:
						return iP1.doubleValue.CompareTo(iP2.doubleValue);
					// 对象引用
					case SerializedPropertyType.ObjectReference:
						return CompareObjects(iP1.objectReferenceValue, iP2.objectReferenceValue);
					case SerializedPropertyType.String:
						return string.Compare(iP1.stringValue, iP2.stringValue, StringComparison.Ordinal);
					default:
						return 0;
				}
			}

			/// <summary>
			/// 比较对象
			/// </summary>
			/// <param name="iObj1">对象1</param>
			/// <param name="iObj2">对象2</param>
			/// <returns>-1:P1大于P2; 0:P1等于P2; 1:P1小于P2;</returns>
			private static int CompareObjects(Object iObj1, Object iObj2) 
			{

				if (iObj1 && iObj2) 
				{
					return string.Compare(iObj1.name, iObj2.name, StringComparison.Ordinal);
				}

				if (iObj1) 
				{
					return 1;
				}
				return 0;
			}
    }
}
